home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------------
- *
- * Apple Developer Technical Support
- *
- * Text section handling routines
- *
- * Program: AEObject-Edition Sample
- * File: TextSections.c - C Source
- *
- * by: C.K. Haun <TR>
- *
- * Copyright © 1990-1992 Apple Computer, Inc.
- * All rights reserved.
- *
- *------------------------------------------------------------------------------
- * This file contains the functions for manipulating and bookkeeping
- * for published and subscribed text sections in this sample.
- *----------------------------------------------------------------------------*/
-
- #define __TEXTSECTIONS__
-
- #pragma segment TextHandler
- #include "Sampdefines.h"
-
-
- static Boolean Touching(mySectionDataHandle checkSection, TEHandle teText);
- extern void MyHiHook(void);
-
- Rect nulRect={0,0,0,0};
- /* the following few functions are for text, and will be expanded as this is fully implemented */
- Boolean HasTESelection(windowCHandle inWind)
- {
- TEHandle temp;
- if (inWind == nil)
- return(false);
- temp = (*inWind)->boxHandle;
- if (temp != nil)
- return((*temp)->selStart != (*temp)->selEnd);
- else
- return(false);
- }
-
- Boolean PasteText(void)
- {
- windowCHandle tempWC;
- tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
- if ((*tempWC)->boxHandle != nil && gClipHasContents == kClipHasText) {
- TEFromScrap();
- CheckTextSections(tempWC, kAdding);
- TEPaste((*tempWC)->boxHandle);
- return(true);
- }
- return(false);
- }
-
- Boolean CutText(void)
- {
- windowCHandle tempWC;
- tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
- if (HasTESelection(tempWC)) {
- CheckTextSections(tempWC, kRemoving); /* adjusts the end points on text sections */
- TECut((*tempWC)->boxHandle);
-
- ZeroScrap();
- TEToScrap();
- return(true);
- }
- return(false);
- }
-
- Boolean CopyText(void)
- {
- windowCHandle tempWC;
- tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
- if (HasTESelection(tempWC)) {
- TECopy((*tempWC)->boxHandle);
- ZeroScrap();
- TEToScrap();
- return(true);
- }
- return(false);
- }
-
- Boolean ClearText(void)
- {
- windowCHandle tempWC;
- tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
- if (HasTESelection(tempWC)) {
- CheckTextSections(tempWC, kRemoving);
- TEDelete((*tempWC)->boxHandle);
-
- return(true);
- }
- return(false);
- }
-
- /* This checks all the publishers and subscribers in the current TE record, and sees */
- /* if their data needs to be modified based on the TE action pending */
- void CheckTextSections(windowCHandle inWindow, short Action)
- {
- TEHandle theTEHandle = (*inWindow)->boxHandle;
- mySectionDataHandle startSection = (*inWindow)->textSections;
- Boolean testFlag = true; /* a gen purp flag please */
- Boolean whapBorder = false;
- mySectionDataHandle currentSection = startSection;
- short counter = 0;
- /* see if the insertion point or selection range is after all our sections. */
- /* if it is (a forlorn hope) we can exit without adjusting anything. */
- if (startSection == nil)
- return; /* no sections, go away */
- do {
- if ((*currentSection)->endChar > (*theTEHandle)->selStart) {
- testFlag = false;
- counter++; /* a minimal aid to exiting this stuff later */
- if((*currentSection)->bordered)whapBorder=true;
- }
- currentSection = (*currentSection)->nextSection;
- } while (currentSection);
- if (testFlag)
- return; /* no section is affected by this action, go away */
-
- /* OK, someone (or two, or twenty) will be affected by this action */
- /* so we have to see who, and how much, and if it's like adding in the middle, */
- /* or like shifting start and end points down, or like whatever, y'know? */
-
- currentSection = (*inWindow)->textSections;
- switch (Action) {
- unsigned long actionLen;
- case kKeyStroke:
- /* so we bump the start and end positions of all the sections after this point by 1 */
- while (counter) { /* minimal aid in exiting this thing */
-
- if ((*theTEHandle)->selStart <= (*currentSection)->startChar) {
- (*currentSection)->startChar++;
- (*currentSection)->endChar++;
- counter--;
- } else {
- if ((*theTEHandle)->selStart > (*currentSection)->startChar &&
- (*theTEHandle)->selStart < (*currentSection)->endChar) {
- /* this should mean that the input char is between the start and the end */
- /* so the publisher expands by 1 */
- (*currentSection)->endChar++;
- counter--;
- }
- }
- currentSection = (*currentSection)->nextSection;
- if (currentSection == nil)
- break; /* hey, y'know? */
- }
- break;
- case kAdding:
- /* increase start and finished positions by the size of the clip being added */
- actionLen = TEGetScrapLength();
- /* so we bump the start and end positions of all the sections after this point by 1 */
- while (counter) { /* minimal aid in exiting this thing */
- if ((*currentSection)->startChar > (*theTEHandle)->selStart) {
- (*currentSection)->startChar += actionLen;
- (*currentSection)->endChar += actionLen;
- counter--;
- } else {Boolean temp=false;
- /* expand the published section */
- temp=(*currentSection)->bordered;
- (*currentSection)->endChar += actionLen;
-
- }
- currentSection = (*currentSection)->nextSection;
- if (currentSection == nil)
- break; /* hey, y'know? */
- }
- break;
- case kRemoving:
- /* decrease start and finished positions by the size of the current selection */
- /* You have another worry in this area, of course. If the user has highlighted */
- /* the WHOLE publisher or subscriber, that means that you will be deleting */
- /* it completely */
- actionLen = (*currentSection)->endChar - (*currentSection)->startChar;
- /* so we bump the start and end positions of all the sections after this point by 1 */
- while (counter) { /* minimal aid in exiting this thing */
- if ((*currentSection)->startChar > (*theTEHandle)->selStart) {
- (*currentSection)->startChar -= actionLen;
- (*currentSection)->endChar -= actionLen;
- counter--;
- }
- currentSection = (*currentSection)->nextSection;
- if (currentSection == nil)
- break;
- }
- break;
- }
- /* if anything changed and borders are on in any section, we need to */
- /* erase and redraw the borders */
- /* This will cause a lot of screen flicker, you can do it neater in your app */
- if(whapBorder)InvalRect(&(*theTEHandle)->viewRect);
- }
- /* KillTextBorders does about what you'd expect, it turns off all the */
- /* text borders */
- void KillTextBorders(void)
- {windowCHandle tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
- mySectionDataHandle startSection = (*tempWC)->textSections;
- while(startSection){
- (*startSection)->bordered = false;
- startSection = (*startSection)->nextSection;
- }
- }
-
- void ShowAllTextBorders(void)
- {windowCHandle tempWC = (windowCHandle)GetWRefCon(FrontWindow() );
- mySectionDataHandle startSection = (*tempWC)->textSections;
- while(startSection){
- (*startSection)->bordered = true;
- startSection = (*startSection)->nextSection;
- }
-
-
- }
- /* SkipOverSubscriber is used to move the insertion point around any subscriptions */
- /* in response to a cursor or delete key action */
- /* This is, of course, because you cannot edit the contents of a subscription */
- Boolean SkipOverSubscriber(windowCHandle inWindow, unsigned short theKey)
- {
- long oldStart, oldEnd;
- Boolean flagBack = false;
- short newInsertionPoint; /* where the insertion point will be moving to as */
- /* a result of this key */
- /* first, as usually, see if this action impinges on the target */
- /* of course, this only counts for subscriptions */
- TEHandle theTEHandle = (*inWindow)->boxHandle;
- mySectionDataHandle startSection = (*inWindow)->textSections;
- if (!startSection)
- return(flagBack); /* scat if no sections */
- do {
- /* first see if it's a publisher, if it is, skip it. */
- if (!(*startSection)->publishing) {
- switch (theKey) { /* actions based on what direction this key is moving us */
- case kLeftArrow:
- newInsertionPoint = ((*theTEHandle)->selStart) - 1;
- if (newInsertionPoint <= (*startSection)->endChar && newInsertionPoint >= (*startSection)->startChar) {
- flagBack = true;
- TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle);
- }
- break;
-
- case kRightArrow:
- newInsertionPoint = ((*theTEHandle)->selEnd) + 1;
- if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) {
- flagBack = true;
- TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle);
- }
- break;
- case kUpArrow:
- case kDownArrow:
- /* for up and down arrows, we actually have to do the TEKey thing here, then evaluate the */
- /* result and see if it landed in a subscription. This is of course because we're using */
- /* TextEdit, if you're doing your own text processing you will know where any keystroke lands */
- /* Of course, you gotta save the current position of the insertion point first */
- oldStart = (*theTEHandle)->selStart;
- oldEnd = (*theTEHandle)->selEnd;
- TEKey(theKey, theTEHandle);
- /* see where it ended up */
- newInsertionPoint = (*theTEHandle)->selEnd;
- if (newInsertionPoint >= (*startSection)->startChar && newInsertionPoint <= (*startSection)->endChar) {
- flagBack = true;
- /* now move before or after the section depending on the arrow */
- if (theKey == kUpArrow)
- TESetSelect(((*startSection)->startChar), ((*startSection)->startChar), theTEHandle);
- else
- TESetSelect(((*startSection)->endChar), ((*startSection)->endChar), theTEHandle);
- } else {
- TESetSelect(oldStart, oldEnd, theTEHandle);
- }
- break;
-
- }
- }
- startSection = (*startSection)->nextSection; /* go to next in list */
- } while (startSection);
-
- return(flagBack);
- }
-
- /* RePackText makes sure we have the proper text in our lil publisher before writing the */
- /* thing out. */
- /* Actually, since this function exists, we don't really need to keep a copy of the text attached */
- /* to the publisher. */
- void RePackText(mySectionDataHandle currentSection, TEHandle theTEHandle)
- {
- short theLength;
- CharsHandle theRawText;
- Ptr src;
- theLength = (*currentSection)->endChar - (*currentSection)->startChar;
- SetHandleSize((*currentSection)->additionalData, theLength);
- HLock((*currentSection)->additionalData);
- theRawText = TEGetText(theTEHandle);
- HLock((Handle)theRawText);
- src = (Ptr)*theRawText;
- src = src + (*currentSection)->startChar;
- BlockMove((Ptr)((Ptr)*theRawText)+(*currentSection)->startChar, (Ptr)*((*currentSection)->additionalData), theLength);
- HUnlock((Handle)theRawText);
- HUnlock((*currentSection)->additionalData);
- }
-
- /* InTextBox tells us if the rectangle passed intersects the text rectangle */
- /* in the current window. We use this for; */
- /* 1) Seeing if the mouse click was in the TERect, so we can call TEClick */
- /* 2) See if one of our drawing commands will overdraw the text, which is BaD */
- Boolean InTextBox(windowCHandle inName, Rect *where)
- {
- Rect temp;
- if ((*inName)->boxHandle == nil) {
- return(false);
- } else {
- return(SectRect(where, &(*inName)->textBox, &temp));
- }
- }
-
- /* CheckSubscriberHit sees if the user person clicked inside a subscription. If they did, then */
- /* we'll either highlight the whole subscriber and <sometime> draw a border around it, or */
- /* extend the selection range to include the whole subscription if they dragged into just part of it */
- /* for bording, things get a little more complicated. If this is a publisher, and it's already */
- /* showing a border, we want to do a normal TEClick since the user can edit inside a publisher */
- void CheckSectionHit()
- {
- windowCHandle shortName = (windowCHandle)GetWRefCon(FrontWindow());
- TEHandle theTEHandle = (*shortName)->boxHandle;
- long mySelStart = (*theTEHandle)->selStart; /* added these variables just to make the code a */
- long mySelEnd = (*theTEHandle)->selEnd; /* little easier to read */
- long newStart = mySelStart; /* if any adjustments need to be made */
- long newEnd = mySelEnd;
- Boolean startedBordered;
- mySectionDataHandle startSection = (*shortName)->textSections;
- mySectionDataHandle currentSection = startSection;
- /* and of course, the affected window is the front window, since they clicked there, y'know */
- /* what we do here is see if the current mouse click lands in a publisher or */
- /* subscriber. If it's a publisher, check for a double click and highlite. */
- /* if the selection range covers ANY part of a subscriber, extend the selection to */
- /* encompass the whole subscriber. Then border the subscriber also. */
- /* This means that we cannot exit after finding one pub or sub, we must */
- /* check all since the user could have selected all the text in the record */
- while (currentSection) {
- startedBordered = (*currentSection)->bordered;
- if (Touching(currentSection, theTEHandle)) {
- /* see if this is a subscriber. If so, extend start and end positions to include it all */
- if (!(*currentSection)->publishing) {
- /* it is a subscriber */
- if ((*currentSection)->startChar < mySelStart)
- newStart = (*currentSection)->startChar;
- if ((*currentSection)->endChar > mySelEnd)
- newEnd = (*currentSection)->endChar;
- TESetSelect(newStart, newEnd, theTEHandle);
-
- } else {
- /* for a publisher, highlith the whole thing if it's the first click. If it's the */
- /* second (or subsequent, o' course) they want to edit, so let them */
- if (startedBordered)
- TESetSelect(newStart, newEnd, theTEHandle);
- else
- TESetSelect((*currentSection)->startChar, (*currentSection)->endChar, theTEHandle);
- }
- } else {
- /* if this section was not hit, deborder it */
- if(!gShowingAll && (*currentSection)->bordered){(*currentSection)->bordered=false;
- InvalRect(&(*theTEHandle)->viewRect);
- }}
- currentSection = (*currentSection)->nextSection;
- }
-
-
-
- }
-
- /* Touching was created just so I wouldn't have to have a huge if statement in */
- /* CheckSectionHit */
- /* All it does is see if the current selection range of the TEHandle touches or */
- /* fully encloses any text sections */
- static Boolean Touching(mySectionDataHandle checkSection, TEHandle teText)
- {
- Boolean ouch = false; /* will turn true if a touch actually happened */
-
- long tStart = (*teText)->selStart;
- long tEnd = (*teText)->selEnd;
- short ourStart = (*checkSection)->startChar;
- short ourEnd = (*checkSection)->endChar;
- /* first see if the start hits inside the section */
- if (tStart > ourStart && tStart < ourEnd)
- ouch = true;
- else if (tStart < ourStart && tEnd > ourStart)
- ouch = true; /* now see if we got completely enclosed */
- else if (tEnd > ourStart && tEnd < ourEnd)
- ouch = true;
- /* and finally see if we got touched by the end of the thing */
- if (ouch){
- BorderTextSection(checkSection);
- /* put this section in our gloabl section handle holder so we can do options on it */
- gShowingSecHandle=(*checkSection)->theSection;
- if((*checkSection)->publishing) {
- gShowPub = true;
- gShowPubRect=nulRect;
- } else {
- gShowSub = true;
- gShowSubRect=nulRect;
- }}
- return(ouch);
- }
-
- /* ExcludeSubscriber removes subscriptions from the range of selected text, so when a keystroke */
- /* replaces a selection, the subscriber does not also get replaced. */
- void ExcludeSubscriber(windowCHandle tempCH)
- {
- #pragma unused (tempCH)
- }
-
- /* this borders the selected section. It's a pain. */
- /* BUT, it's a pain because I'm using TextEdit. If you're writing a word processor */
- /* or another application which does it's own text processing, this will be a lot easier */
- /* •••• NOTE: this doesn't look very good when a subscriber is embedded in a publisher, since */
- /* the lines of course overlap. I don't know how to deal with this yet. */
- void BorderTextSection(mySectionDataHandle theText)
- {
- TEHandle theTEHandle;
- WindowPtr theWindow;
- windowCHandle shortName;
- PolyHandle borderPoly;
- long origIStart;
- long origIEnd;
- long newStart;
- long newEnd;
- Point startPoint;
- Point endPoint;
- RgnHandle oldClip = NewRgn();
- WindowPtr oldPort;
- GetPort(&oldPort);
- GetClip(oldClip);
-
- theWindow = FindSection((*theText)->theSection);
- SetPort(theWindow);
- shortName = (windowCHandle)GetWRefCon(theWindow);
- HLock((Handle)shortName);
- theTEHandle = (*shortName)->boxHandle;
-
- origIStart = (*theTEHandle)->selStart;
- origIEnd = (*theTEHandle)->selEnd;
- TESetSelect((*theText)->startChar, (*theText)->endChar, theTEHandle);
- newStart = (*theTEHandle)->selStart;
- newEnd = (*theTEHandle)->selEnd;
- ClipRect(&(*theTEHandle)->viewRect);
- /* so our poly doesn't exceed the bounds of our rect */
- /* now what we do is create a Polygon that's the shape of the text section */
- startPoint = TEGetPoint((short)newStart, theTEHandle);
- endPoint = TEGetPoint((short)newEnd, theTEHandle);
- /* •••• NOTE: This is a really simplistic check, since this is a simple sample. */
- /* In a more complex text document you'd be worrying about text scrolled out of the view rect, */
- /* weird regions, and other things I'm not going to deal with here. */
- /* see if the verts are the same. If they are, then all we need is a simple rect */
-
- if (startPoint.v == endPoint.v) {
- borderPoly = OpenPoly();
- startPoint.v -= (*theTEHandle)->lineHeight; /* top o' the line to you! */
-
- MoveTo(startPoint.h, startPoint.v);
- LineTo(endPoint.h, startPoint.v);
- LineTo(endPoint.h, endPoint.v);
- LineTo(startPoint.h, endPoint.v);
- LineTo(startPoint.h, startPoint.v);
- ClosePoly();
- PenSize(3, 3);
- if ((*theText)->publishing)
- PenPat(&qd.gray);
- else
- PenPat(&qd.dkGray);
- FramePoly(borderPoly);
- PenPat(&qd.black);
- PenSize(1, 1);
-
- } else {
- short endTop = endPoint.v -(*theTEHandle)->lineHeight;
- /* need to build a slightly more complex poly */
- borderPoly = OpenPoly();
- startPoint.v -= (*theTEHandle)->lineHeight; /* top o' the line to you! */
-
- MoveTo(startPoint.h, startPoint.v);
- LineTo((*theTEHandle)->destRect.right, startPoint.v);
- LineTo((*theTEHandle)->destRect.right, endTop);
- LineTo(endPoint.h, endTop); /* may not be needed, but doesn't hurt anything */
- LineTo(endPoint.h, endPoint.v);
- LineTo((*theTEHandle)->destRect.left, endPoint.v);
- LineTo((*theTEHandle)->destRect.left, (startPoint.v + ((*theTEHandle)->lineHeight)));
- LineTo(startPoint.h, (startPoint.v + ((*theTEHandle)->lineHeight)));
- LineTo(startPoint.h, startPoint.v);
- ClosePoly();
- PenSize(3, 3);
- if ((*theText)->publishing)
- PenPat(&qd.gray);
- else
- PenPat(&qd.dkGray);
- FramePoly(borderPoly);
- PenPat(&qd.black);
- PenSize(1, 1);
-
- }
- if((*theText)->bordered)
- /* if it started bordered, and it's a publisher, then when set the selection range to */
- /* what it was when we entered */
- TESetSelect(origIStart,origIEnd, theTEHandle);
- else
- (*theText)->bordered = true; /* log it bordered */
- KillPoly(borderPoly);
- SetClip(oldClip);
- DisposeRgn(oldClip);
- SetPort(oldPort);
- HUnlock((Handle)shortName);
- }
-
-
- #undef __TEXTSECTIONS__
-